home *** CD-ROM | disk | FTP | other *** search
/ Celestin Apprentice 5 / Apprentice-Release5.iso / Source Code / C / Frameworks / Extension Shell 1.5 / INIT Writing FAQ 1.0.1 < prev    next >
Text File  |  1996-04-12  |  66KB  |  1,538 lines

  1. ****
  2. **** Please Note
  3. ****
  4. **** This may be an old copy of the FAQ. It is only updated
  5. **** as and when Extension Shell is updated. Feel free to
  6. **** Archie for and download newer versions of the FAQ,
  7. **** but please don't email me to let me know - when I
  8. **** bring out a new release of Extension Shell, I'll
  9. **** update the FAQ with the more recent copy.
  10. ****
  11. **** -dair
  12. **** dair@kagi.com
  13. ****
  14.  
  15.  
  16.  
  17.  
  18.  
  19. Answers to Frequently Asked Questions about writing System Extensions on
  20. the Macintosh Computer. Version 1.0.1 11/94
  21.  
  22. This document is Copyright © 1994 by Brian Stern.
  23. I can be contacted by email at <BrianS@pbcomputing.com> on Internet.
  24.  
  25. The purpose of this FAQ list is to provide information on the writing of
  26. system extensions.  This arcane art is difficult to learn and the existing
  27. information on the subject is spread around in various places.  Hopefully
  28. the information here will help new and old INIT writers to write better
  29. INITs in less time.
  30.  
  31. The questions in this document are broken into two sections: System
  32. Extensions and Trap Patches.  While most extensions contain trap patches
  33. these subjects seemed different so I have separated them.
  34.  
  35. FAQ lists like this one are an integral part of Usenet.  When I started
  36. reading Usenet news I assure you that I knew nothing about writing
  37. extensions.  I learned by posting questions to comp.sys.mac.programmer and
  38. by reading other's questions and responses.  Let me take this space to give
  39. you a few tips on phrasing of questions to newsgroups.
  40.  
  41. When asking a question try to make the title as descriptive as possible. 
  42. Most people don't have time to read every question posted and will ignore
  43. posts whose titles are unclear or meaningless.  Don't post questions titled
  44. 'HELP, My program doesn't work', or worse 'URGENT NEED HELP with program'. 
  45. I'm sure it's urgent to you, but not to me.  Always indicate that you're
  46. asking a questing by including a (you guessed it) question mark.  Consider
  47. these titles: 'Programmers make big salaries' and 'Programmers make big
  48. salaries?'  Don't waste people's time with the former when you mean the
  49. latter.  Another way to indicate a question is like this: '[Q] Programmers
  50. make big salaries?'.  Try to include one of the words 'who, what, when,
  51. where, why, or how' in the titles of your questions.  Your questions are
  52. much more likely to be answered by someone who actually knows the answer if
  53. your title is clear.
  54.  
  55. The code samples in this document were developed with Think C.  You may
  56. need to make some changes to use them with other development systems.  I've
  57. used inline assembly in many of the code samples.  To my eye this makes
  58. things more clear since it's obvious what's going on.  Not everyone agrees
  59. with this and not all development environments provide inline assembly. 
  60. It's possible to rewrite most or all of this code by use of inline
  61. functions.  If you're writing in Pascal or using CodeWarrior you'll have to
  62. do it that way.
  63.  
  64. You'll be able to find information on general questions of Mac programming
  65. in the FAQ list maintained by Jon Watte at:
  66. ftp://nada.kth.se/pub/hacks/mac-faq/CSMP_PD_FAQ
  67.  
  68. If you have questions about writing extensions that aren't addressed in
  69. this document or any comments about it feel free to send them to me.  If I
  70. know the answer or can find it out it may find its way into a later
  71. version.
  72.  
  73. This document may be distributed freely as long as no changes are made to
  74. it.  It may not be distributed in ways in which the user must pay for the
  75. document, other than reasonable download costs or costs for the medium,
  76. without the consent of the author.
  77.  
  78. Thanks to the following people who provided sample code and made
  79. constructive comments: Pete Gontier, Dair Grant, Chelly Green, Devon
  80. Hubbard, Peter Lewis, Jim Walker, Jim Wintermyre.
  81.  
  82. ---------------------------------------------------------------------
  83. System Extension Writing FAQ Outline:
  84.  
  85. System Extensions:
  86.  
  87. [1] What is an INIT, exactly?
  88. [2] Should I write an INIT?
  89. [3] Can I write an INIT that makes the application menu into a hierarchical
  90. menu?
  91. [4] Do I need to know assembler to write an INIT?
  92. [5] Can you show me a sample INIT?
  93. [6] Can I have global variables in my INIT?
  94. [7] How do I debug an INIT?
  95. [8] How do I write a Control Panel-INIT combination?
  96. [9] How do I capture keystrokes?
  97. [10] How can my extension get time periodically?
  98. [11] How should an INIT manage memory?
  99. [12] How do I get my INIT to turn itself off?
  100. [13] How do I get my INIT to show its icon like all the other cool INITs
  101. do?
  102. [14] How do I show a dialog from my INIT?
  103. [15] How do I maintain compatibility with future systems?
  104. [16] Any tips for INIT writing?
  105.  
  106. Trap Patches:
  107.  
  108. [17] What exactly is a trap patch?
  109. [18] What's the difference between a head patch and a tail patch?
  110. [19] How do I patch a trap?
  111. [20] How do I patch a register-based trap?
  112. [21] Can you show me a tail patch? [22] How do I patch a selector-based
  113. trap?
  114. [23] How do I patch a trap on the PPC?
  115. [24] Can I write a fat trap?
  116. [25] Tips?
  117. [26] What other sources of information are available?
  118.  
  119. ---------------------------------------------------------------------
  120.  
  121. [1] What is an INIT, exactly?
  122.  
  123. An INIT is a type of code resource that is loaded into memory and executed
  124. during the startup process.  They're called INITs because they're resources
  125. of type 'INIT'.  INITs load at the end of the startup process, after the
  126. hardware checks have been done and the system has been started.  The span
  127. of time during which INITs load and execute is known as INIT time. 
  128. Applications don't load until after INIT time and in most cases the first
  129. application to load is the Finder.
  130.  
  131. Because they load before all applications, INITs can modify the system in a
  132. way that affects all applications.  They can add new functionalities and
  133. modify the way in which many processes occur on the Mac.  Apple often
  134. supplies new additions and updates to system software as INITs.  This
  135. includes things such as the Drag-and-Drop Manager, Thread Manager, Speech
  136. Manager, and Sound Manager 3.0.  At some point these new features are
  137. rolled into a new software release and eventually they are included in new
  138. ROMs, but they all start life as INITs.
  139.  
  140. Apple recommends that the term 'system extension' be used when
  141. communicating with non-programmers.  This name carries the implication that
  142. they extend the functionality of the system.  You'll see the terms 'INIT',
  143. 'extension', and 'system extension' used to mean the same thing throughout
  144. this document.
  145.  
  146. Extensions are loaded in a defined order.  Resources of type 'INIT' can be
  147. found in files of type 'INIT' (System Extension), 'cdev' (Control Panel),
  148. 'RDEV' (Chooser Device), 'appe' (application extension), and 'scri' (script
  149. system extensions).  In order to be loaded, the INIT resource must be in
  150. one of these file types in one of several locations in the System Folder.  
  151.  
  152. Under system 7 INITs are loaded first from the Extensions Folder in
  153. alphabetical order.  INITs present in script system extension files
  154. (filetype 'scri') load before INITs present in system extension files
  155. (filetype 'INIT').  Next, any INITs in the Control Panels folder are loaded
  156. in alphabetical order.  Any INITs present at the root level of the System
  157. Folder are loaded after that.  This order of loading is important if an
  158. extension is dependent on the presence of another extension.  For example,
  159. if an INIT resource in an INIT file named 'SpeakAll' is dependent on the
  160. presence of the 'Speech Manager' extension, it either needs to have its
  161. name changed to something that sorts after 'Speech Manager' or it has to be
  162. located in the Control Panels folder, since extensions in the Control
  163. Panels folder load after extensions in the Extensions Folder. 
  164.  
  165. In system 6 the Extensions Folder and Control Panels Folders don't exist. 
  166. Extensions are simply loaded in alphabetical order from the root level of
  167. the System Folder.
  168.  
  169. --
  170. [2] Should I write an INIT?
  171.  
  172. System extensions are not easy to write.  If this is your first attempt at
  173. Mac programming, the answer to this question is NO.  Many experienced Mac
  174. programmers have never written one and don't intend to.  
  175.  
  176. There are a number of other ways to add functionality without writing an
  177. extension.  If you want to add functionality to a single application then
  178. think about writing an FKEY.  These are invoked by hitting cmd-shift-number
  179. and perform an action at that time only.  The standard screen shot
  180. (cmd-shift-3) is one example.
  181.  
  182. Another user-invokable means of adding functionality is plug-in modules.  A
  183. number of commercial software packages support plug-ins that can add
  184. functionality in a straightforward manner.  This includes PhotoShop, Quark
  185. Express, Hypercard and others.
  186.  
  187. Another means of adding functionality is the use of a background-only
  188. application, AKA faceless-background application (fba).  Fbas are
  189. applications without a user interface.  One type of fba is known as an
  190. application extension.  These are applications whose resource files are of
  191. type 'appe'.  They are placed in the extensions folder (or the Startup
  192. Items Folder) and are started up after INIT time.  They can communicate
  193. with other processes by Apple Events and by Gestalt selectors.  If you
  194. don't need to patch a trap then an fba may be the way to add functionality
  195. to the system.  Since an fba runs as a normal process there are some things
  196. that it can do that system extensions cannot do (or at least cannot do
  197. safely), like launch applications and send and receive Apple Events.  An
  198. INIT resource in the resource fork of an application extension in the
  199. Extensions Folder will load normally at INIT time.  See issue 9 of develop
  200. and the Tech Note PS 2 - Background-Only Applications for more info.
  201.  
  202. Also consider an application that is started up by being placed in the
  203. startup items folder.  The Screen Saver 'Dark Side of the Mac' is written
  204. in this way.  Screen Savers are traditionally written as extensions because
  205. extensions can have access to the mouse location and all keyboard events. 
  206. However an intelligently written application can do the same and be more
  207. compatible.  
  208.  
  209. If none of these things seems like it will work for you and you are
  210. thinking something like 'I want X to happen every time a resource is
  211. loaded', or otherwise add to or replace the system's functionality in some
  212. way then an extension is probably what you need.
  213.  
  214. Remember that when developing system extensions you will be working without
  215. a net.
  216.  
  217. --
  218. [3] Can I write an INIT that makes the application menu into a hierarchical
  219. menu?
  220.  
  221. Every couple of weeks someone posts a message on comp.sys.mac.programmer
  222. entitled 'Neat idea for an init'.  These posts go on to describe some
  223. non-programmer's idea of a great INIT.  Invariably these posts fall into
  224. two groups: those that have already been done, and those that should never
  225. be done.  Think long and hard about the design of an extension before
  226. starting to write it.  Consider your target audience.  If it's only
  227. yourself then you can do what you want.  My first couple of extensions were
  228. just tests to see if I could actually do it.  The answer to the above
  229. question is: Maybe you can but probably you shouldn't.
  230.  
  231. --
  232. [4] Do I need to know assembler to write an extension?
  233.  
  234. Yes.  Strictly speaking, the simplest extension that just beeps at startup
  235. and doesn't hang around past INIT time requires no assembler.  However, any
  236. extension that does anything useful will require some assembler in order to
  237. patch a trap or install a jGNEFilter.  The extension can be written mostly
  238. in a high level language like C or Pascal, but there will be bits and
  239. pieces that need to be written in assembler to keep the stack happy or to
  240. access parameters passed in registers.  Hopefully there will be enough
  241. sample code in this document to get you on your way if your assembler is
  242. weak.
  243.  
  244. You will often need to inspect the disassembled source of your extension. 
  245. If your development environment doesn't allow this the ResEdit Code Viewer
  246. will also allow you to inspect code resources.  It can be found at
  247. ftp://ftp.apple.com/dts/mac/tools/resedit/resedit-extensions.hqx.
  248.  
  249. --
  250. [5] Can you show me a sample INIT?
  251.  
  252. Here's the code for the 'Hello World' of INITs.
  253.  
  254. void main(void)
  255. {
  256.     SysBeep( 5 );
  257. }
  258.  
  259. Not very complicated is it?  There are a number of additional things that
  260. are required to make this work:
  261.  
  262. 1) Set the project type to code resource.
  263. 2) Set the resource type to 'INIT'.
  264. 3) Set the resource ID to something (>= 128), and set the resource name if
  265. desired.
  266. 4) Set the resource attributes to 'System Heap'.
  267. 5) Set the resource attributes to 'Locked'.
  268. 6) Set the filetype of the built code resource file to 'INIT' with any
  269. creator signature.
  270. 7) Set the filename of the built code resource file to 'Hello World'.
  271. 8) Include a 'sysz' resource indicating a size of 100K.
  272.  
  273. Let me explain what each of these things does.  
  274.  
  275. 1)  Because you're building a standalone code resource your compiler needs
  276. to know this.  This is how the compiler knows to use A4 addressing, rather
  277. than the A5 addressing used in applications.
  278.  
  279. 2)  As mentioned above, extensions are code resources of type 'INIT'.  
  280.  
  281. 3)  Like any other resources they have IDs and can have names.  
  282.  
  283. 4)  Setting the system heap flag places the code resource in the system
  284. heap.  While not strictly required for this sample, in most other
  285. extensions we will want the code resource to remain in the system heap so
  286. this flag must be set.  During the process that loads and executes INITs a
  287. small heap is created.  Any memory allocation done by the INIT will, by
  288. default occur in this heap.  Also any resources read into memory will, by
  289. default, go into this heap.  If the system heap flag isn't set for the INIT
  290. itself then the INIT will be loaded into this temporary heap.  When the
  291. INIT exits the heap is disposed and anything in it is lost.  If an INIT
  292. intends to stay around past INIT time then it must have the system heap
  293. flag set.
  294.  
  295. 5)  The code resource should never move in memory so the Locked bit is set.
  296.  When a resource with the Locked bit is loaded into memory it is loaded as
  297. low in the heap as possible.  This is what we want since the INIT resource
  298. will never be unlocked.  It is poor design to not set the Locked bit and to
  299. rely on the INIT to lock itself.  In that case the INIT will not be loaded
  300. low in the system heap.  This isn't fatal but will lead to fragmentation of
  301. the system heap.  
  302.  
  303. Some INITs try to use MoveHHi/HLock to move themselves or associated
  304. resources high in the system heap.  MoveHHi is disabled for the system
  305. heap.  Since the system heap is dynamically sizable the concept of the top
  306. of the heap is invalid.  For this reason MoveHHi does nothing when the
  307. handle being referred to is within the system heap.  
  308.  
  309. 6)  The filetype of 'INIT' gives you the generic extension icon, and of
  310. course allows the extension to load and execute at INIT time.
  311.  
  312. 7)  Set the file name to something like 'Hello World'.
  313.  
  314. 8)  A 'sysz' resource indicates to the INIT-loading code that your INIT
  315. requires a contiguous block of memory of the indicated size.  The system
  316. heap will be expanded if necessary to make certain that a block of the
  317. requested size is available.  If no 'sysz' recource is included the system
  318. assumes a block of 16K is sufficient.  The format of the 'sysz' resource is
  319. simply a long integer indicating the blocksize.  You can create one with
  320. ResEdit, or copy one from another INIT and edit it with the hex editor. 
  321. This sample extension requires more than 16K because SysBeep allocates a
  322. sound channel and because you don't know what the size of the 'snd '
  323. resource is for the system alert sound.  If no 'sysz' resource has been
  324. included this INIT sometimes doesn't work because there isn't enough room
  325. to allocate the sound channel.
  326.  
  327. Once these things have been done you can build the code resource and drag
  328. its file to the System Folder.  The Finder should place it in the
  329. Extensions Folder for you and it should have the generic extension icon. 
  330. When you restart you will hear the beep when the extension is loaded.
  331.  
  332. Every extension must have a routine called 'main'.  When the INIT resource
  333. is loaded into memory during INIT time the system jumps to the beginning of
  334. the code resource.  The header of the code resource will normally contain a
  335. branch instruction that branches to the main routine.  This routine does
  336. whatever it needs to do and then returns.  In this 'Hello World' extension
  337. all that the main routine does is call SysBeep.  In more substantial
  338. extensions the main routine will do things like patch traps, install
  339. gestalt selectors, and load resources into memory.
  340.  
  341. --
  342. [6] Can I have global variables in my INIT?
  343.  
  344. Yes you can.  The Think environment and the CodeWarrior environment use
  345. A4-based addressing for global variables in code resources.  The base
  346. address of the extension is found in register A0 on entry to the extension,
  347. and routines are provided to save this value and to set up and restore
  348. register A4 when the INIT is entered later.  (In fact the standard header
  349. installed by Think C in code resources loads the address of the code
  350. resource into A0 by use of pc-relative addressing.)  See the A4-Addressing
  351. section of your development environment's manual for more information.  
  352.  
  353. When writing extensions with the MPW compilers you may need to use an
  354. A5-based method for accessing global variables.  See the Apple tech note
  355. 'StandAlone Code, ad nauseam' (#256) for more information on this method. 
  356. ftp://ftp.apple.com/dts/mac/tn/platforms.tools.pt/pt-35-stand-alone-code.hqx
  357.  
  358.  
  359. and 
  360.  
  361. "Another take on Globals in Standalone Code", Keith Rollin
  362. ftp://ftp.apple.com/dts/mac/docs/develop/develop.12.code/globals-in-standalone-code.hqx
  363.  
  364. Here is some sample code using the Think C routines to illustrate this:
  365.  
  366. /****Main*****************************************************/
  367. void 
  368. main(void)            //Main entry point of the extension
  369. {
  370.     void        *pToMe;
  371.     Boolean    initedOK;
  372.  
  373.     pToMe = GetA0();            //Save our address locally
  374.  
  375.     RememberA0();            //Save our base address
  376.     SetUpA4();                //Set up A4-based addressing
  377.  
  378.     initedOK = InitAll();        //Patch traps etc.
  379.     if ( initedOK )
  380.     {
  381.                         //Make sure we stay around
  382.         DetachResource( RecoverHandle( pToMe) );    
  383.     }
  384.  
  385.     RestoreA4();            //Reset A4 to its value on entry
  386. }
  387.  
  388. This code sample also illustrates one method extensions can use to remain
  389. in memory after INIT time.  Since extensions are code resources they will
  390. be removed from memory when their resource files are closed.  This happens
  391. when the main routine exits.  To avoid this, DetachResource must be called
  392. with the handle to the code resource.  One common mistake with this is not
  393. having the code resource marked system heap.  If it's not marked system
  394. heap it will be lost when the temporary heap is destroyed, whether it was
  395. detached or not.  The GetA0 routine used above is defined in the tips
  396. section farther down in this document.  Here are a few other methods for an
  397. extension to detach itself:
  398.  
  399. void 
  400. main(void)        //Another method
  401. {
  402.     asm
  403.     {
  404.         RecoverHandle        //A0 already holdss our address
  405.         move.L    A0, -(A7)    //DetachResource is stack-based
  406.         DetachResource        //so push A0 onto the stack
  407.     }
  408. }
  409.  
  410. void 
  411. main(void)        //This method uses no assembler
  412. {
  413.     DetachResource( Get1Resource( 'INIT', kOurResID ) );
  414. }
  415.  
  416. --
  417. [7] How do I debug an INIT?
  418.  
  419. Debugging is generally the most time consuming, difficult, and all-around
  420. pain-in-the-neck part of producing good code.  That goes double for INITs. 
  421. Unfortunately many parts of INITs must be debugged with low level
  422. debuggers.  The low level debuggers that I know about are MacsBug, TMON,
  423. and Jasik's Debugger.  MacsBug is free and is available at
  424. ftp://ftp.apple.com/dts/mac/tools/macsbug/macsbug-6-5d6.hqx.  The two other
  425. low level debuggers are commercialware.  Jasik's debugger has the ability
  426. to do source level debugging of code resources, like system extensions.
  427.  
  428. In most cases, your low level debugger of choice will load before INIT time
  429. so it can be used to debug initialization of INITs.  One exception to this
  430. is Jasik's Debugger, which loads in two parts.  One of these is an
  431. extension that must load before your extension.  
  432.  
  433. The debugger can be invoked by calling the Debugger() or DebugStr() traps. 
  434. Placing these trap calls in your code will drop you into the low level
  435. debugger so that you can step through your code and inspect registers or
  436. memory as needed.
  437.  
  438. One useful technique is to take advantage of Macsbug's ability to process
  439. commands after a semicolon in a DebugStr call.  The following function can
  440. display information that you would otherwise have to hunt down using hex
  441. offsets:
  442.  
  443. pascal void SomeFunction (arguments)
  444. {
  445.     // ...
  446.  
  447.     asm { MOVE.L fooP, A0 }        // fooP can be any type
  448.     asm { MOVE.L sizeof(*fooP), D0 }
  449.     DebugStr ("\p ; dm rA0 rD0");    // dump (*fooP) in hex
  450.  
  451.     // ...
  452. }
  453.  
  454. If you haven't placed Debugger() calls in your code then you will have to
  455. set a breakpoint in order to step through any trap patches or other parts
  456. of your extension.  Here are a couple of methods to do that.  If you have
  457. patched a trap, say GetResource, if you drop into MacsBug and type 'il
  458. GetResource' MacsBug will begin to disassemble at your patch.  You can then
  459. set a breakpoint by typing 'br GetResource' or br theaddress' where
  460. theaddress is the address in hex of your patch or some part of it.  Type
  461. 'brc' when you want to clear all breakpoints.  Using the A-trap commands
  462. will also work.  'atb GetResource' will set a break and 'atc' will clear
  463. all A-trap breakpoints.
  464.  
  465. If you want to set a breakpoint in some other part of your extension, say
  466. in a jGNEFilter, then you need another way of finding its location in
  467. memory.  In MacsBug type 'hx syszone^' or just 'hx' to set the current heap
  468. to the system heap (where your extension is located).  Type 'br ' (don't
  469. hit return yet) and then type cmd-D to view the names of all the routines
  470. in the system heap that were compiled with MacsBug names turned on (like
  471. yours, right?)  Scroll down until you find the name of your routine.  Hit
  472. enter and the command line should look like 'br yourRoutineName'.  Hit
  473. enter again and the breakpoint will be set.  You can also simply type 'br
  474. yourRoutineName' and MacsBug will find it for you.  The zone must be set to
  475. the zone containing the routine that you want to break on for MacsBug to
  476. find it, so make sure to set the zone to the system zone.
  477.  
  478. Identifying your extension in the system heap is dependent on compiling it
  479. with the MacsBug symbols option turned on.  This inserts Ascii versions of
  480. the names of each function in the compiled code in such a way the MacsBug ,
  481. and other debuggers can find these names.  Jasik's debugger uses a
  482. different method for identifying your code that is based on SYM files,
  483. which are generated by your compiler.  The SYM files allows Jasik's
  484. debugger to do source level debugging of INITs and other code resources.
  485.  
  486. I have used a two-project method, when developing INITs and other code
  487. resources, that helps to cut down debugging time.  Any extension consists
  488. of essentially two parts: the initialization portion and the implementation
  489. portion.  The initialization portion detaches the resource as described
  490. above, shows the icon, and often patches traps.  The implementation portion
  491. contains the actual trap patches.  It is quite possible, and desirable, to
  492. test the implementation portion in the context of an application, rather
  493. than as an INIT.  To accomplish this your project consists of three parts,
  494. which for a simple case would be three files.
  495.  
  496. TesterApp Project:
  497.     SetUpApplication.c    Simple application shell that sets up                     the trap
  498. patches and provides a                         mechanism to call the traps
  499.     TrapPatches.c        Code for the trap patches
  500.  
  501. INIT Project:
  502.     SetUpINIt.c            Standard INIT setup; contains main                         entry
  503. point; patches all necessary                         traps
  504.     TrapPatches.c        Code for the trap patches; same file as
  505.                     in the TesterApp Project
  506.  
  507. The mechanism for calling the traps in SetUpApplication.c is usually a menu
  508. item.  In some cases it might be a dialog with several buttons, each of
  509. which calls a particular trap.  It isn't always necessary to patch traps in
  510. your TesterApplication.  Simply calling the routines that implement the
  511. guts of the trap patches will often be just as good.  Having two projects,
  512. one that builds the tester application and the other that builds the
  513. extension, allows you to save time debugging and to be able to build the
  514. extension at any time.
  515.  
  516. Writing and testing extensions involves multiple rounds of
  517. 'compiling-installing the INIT-rebooting-Stepping through the INIT in a low
  518. level debugger'.  Use of the two-project method will cut down this time.
  519.  
  520. --
  521. [8] How do I write a Control Panel-INIT combination?
  522.  
  523. System extensions generally have no interface.  They do what they do
  524. quietly and the user doesn't want to hear from them.  In many cases the
  525. user needs to set certain preferences.  The natural mechanism for this is
  526. to couple a control panel with an INIT.  The problem of course is how does
  527. the control panel communicate the changes to the INIT.  I won't discuss the
  528. general mechanisms of control panel authoring here (see NIM: More Macintosh
  529. Toolbox, Chapter 8), but there are several mechanisms available for
  530. communicating between extensions and control panels.
  531.  
  532. The simplest mechanism and one that will work in the vast majority of cases
  533. is for the extension to install a Gestalt selector.  This selector returns
  534. the address of a block of memory that holds variables that control the
  535. actions of the extension.  The control panel calls the Gestalt selector,
  536. retrieves the address of the memory block, and alters the values stored in
  537. the block as needed.  In some cases the memory block can contain a function
  538. pointer to a function in the extension that needs to be called from the
  539. control panel.  Here is some sample code:
  540.  
  541. typedef struct CommonInfo{
  542.     void        (*ResetINIT) (void);//function pointer to reset
  543.                           //func
  544.     Boolean    On;
  545. };
  546.  
  547. #define kSignature 'BLAH'    //Should be the sig of the INIT
  548.  
  549. static CommonInfo        gInfo;
  550.  
  551. /**InstallGestaltSelector****************************************/
  552. //In the INIT; runs at INIT time
  553. OSErr
  554. InstallGestaltSelector(void)
  555. {
  556.     OSErr        err;
  557.  
  558.     err = NewGestalt( kSignature, OurSelector );
  559.     
  560.     if ( err == noErr )
  561.     {
  562.         gInfo.ResetINIT = (void *) ResetFunction;
  563.         gInfo.On = TRUE;
  564.     }
  565.  
  566.     return err;
  567. }
  568.  
  569. /**OurSelector**************************************************/
  570. //In the INIT
  571. pascal OSErr 
  572. OurSelector( OSType theSelector, long *theResponse )
  573. {
  574.     SetUpA4();
  575.  
  576.     *theResponse = (long) &gInfo;
  577.     
  578.     RestoreA4();
  579.  
  580.     return noErr;
  581. }
  582.  
  583. /**ResetFunction**************************************************/
  584. //In the INIT
  585. void 
  586. ResetFunction(void)
  587. {
  588.     SetUpA4();
  589.  
  590.     //Do something here
  591.     
  592.     RestoreA4();
  593. }
  594.  
  595. /**Close********************************************************/
  596. //In the Control Panel
  597. void
  598. Close( Boolean IsOn )
  599. {
  600.     long            result;
  601.     CommonInfo        *Info;
  602.     
  603.     //Get address of globals struct from init
  604.     err = Gestalt( kSignature, &result );
  605.     if ( err == noErr )
  606.     {
  607.         Info = (GlobalsType *) result;
  608.         Info->On = IsOn;            //Reset OnOff Boolean
  609.         ( * (*Info).ResetINIT) ();    //Jump to INIT
  610.     }
  611. }
  612.  
  613. If it is possible for an error to occur that prevents the extension from
  614. resetting itself the ResetFunction should return an error code and the
  615. control panel should display an alert indicating the problem.
  616.  
  617. Two additional methods are sometimes used to accomplish communication
  618. between an extension and a control panel:  
  619.  
  620. The first of these is to write a driver that is installed by the extension.
  621.  The driver holds global variables and will return the address of the block
  622. of memory holding these variables in response to i/o, status, or control
  623. calls to the driver.  Sample code showing how to do this is available in a
  624. package called driver-22 written by Pete Resnick to be found at:
  625. ftp://sumex-aim.stanford.edu/info-mac/dev/src/driver-22-c.hqx.gz
  626.  
  627. The second additional method is to use the PPC toolbox for direct
  628. communication.  There is sample code demonstrating this at:
  629. ftp://ftp.apple.com/dts/mac/sc/7.0.samples/init-cdev.hqx.
  630.  
  631. -- [9] How do I capture keystrokes?
  632.  
  633. This is accomplished by writing a jGNEFilter function.  jGNEFilter
  634. functions are called from GetNextEvent and WaitNextEvent just before those
  635. traps return to an application.  They are passed a pointer to the event
  636. record that will be returned to the application.  In order to capture
  637. keystrokes the jGNEFilter would simply check the what field of the event
  638. record looking for keyboard events and would extract the information from
  639. the message field if one were found.  
  640.  
  641. The jGNEFilter mechanism is very powerful and is one way that screensavers
  642. can be implemented.  The filter would save the time of any keyboard events
  643. and would compare the location of the mouse against its previous location
  644. on null events.  If the preset time had elapsed during which no keyboard
  645. events or mouse movement had occurred then the screen saver would activate.
  646.  A more complete discussion of the jGNEFilter is in the next section.
  647.  
  648. If you are thinking of patching WaitNextEvent or PostEvent in order to
  649. capture keystrokes or other events, don't.  Use a jGNEFilter instead.  It's
  650. easier, it's compatible.  It's even documented.  See the Tech Note
  651. 'GetNextEvent; Blinking Apple Menu' (#85).
  652.  
  653. ftp://ftp.apple.com/dts/mac/tn/toolbox.tb/tb-11-getnextevent.hqx
  654.  
  655. --
  656. [10] How can my extension get time periodically?
  657.  
  658. Installing a jGNEFilter is one method of obtaining periodic time.  Since
  659. the jGNEFilter mechanism is dependent on the event processing mechanism,
  660. one problem is that no events may be posted if the mouse is held down for
  661. an extended time.  
  662.  
  663. If your INIT only needs to get time every once in a while then I recommend
  664. that it only do its thing on null events.  On other events it should just
  665. return.  This will have the least impact on the machine's performance.  
  666.  
  667. Remember that your jGNEFilter will be called for EVERY event on the
  668. machine.  Do not do a lot of processing on every event or you will be
  669. slowing down the machine needlessly.  Another way to reduce the frequency
  670. of your extension's processing is to use a simple timer, based on
  671. TickCount().  In this way your processing is only done, say, every 30 or 60
  672. ticks, on null events of course.
  673.  
  674. Sample code demonstrating jGNEFilters can be found in a package called jGNE
  675. Helper by Pete Gontier in the alt.sources.mac archive at:
  676. ftp://ftpbio.bgsu.edu/ftp/pub/alt.sources.mac/vol-01/jgnehelper.cpt.hqx.  
  677.  
  678. Another sample in MPW assembler is at
  679. ftp://ftp.apple.com/dts/mac/sc/snippets/toolbox/jgnefilter.hqx.
  680.  
  681. Here's another example that works in Think C:
  682.  
  683. static    ProcPtr    gOldGNEFilter;
  684.  
  685. /****InstallFilter***********************************************/
  686. //Run this at INIT time
  687. void
  688. InstallGNEFilter (void)
  689. {
  690. /*Save the ProcPtr to the previous jGNEFilter and insert ours*/
  691.     gOldGNEFilter = JGNEFilter;
  692.     JGNEFilter = (ProcPtr) StripAddress( FilterProc );
  693.  
  694.  }
  695.  
  696. /****FilterProc**************************************************/
  697.  
  698. void
  699. FilterProc(void)
  700. {
  701.     EventRecord        *theEvent;
  702.     long            SaveD0;
  703.     
  704.     theEvent = GetA1();    //Move the eventPtr to a variable
  705.     SaveD0 = GetD0();        //Preserve D0
  706.  
  707.     SetUpA4();
  708.  
  709.     switch ( (*theEvent).what ) {
  710.         case nullEvent:
  711.             //Do our thing
  712.             break;
  713.             
  714.         case keyDown:
  715.             //Do something else
  716.             break;
  717.     }
  718.     
  719.     //Execute the previous jGNEFilter
  720.     SetA1( theEvent );    //Restore A1 for the next jGNEFiler
  721.     SetD0( SaveD0);        //Restore D0
  722.     SetA0( gOldGNEFilter );    //Put next jGNEFilter in A0
  723.     
  724.     RestoreA4();
  725.  
  726.     asm{
  727.         Unlk        A6
  728.         Move.W    D0, 4(A7)    ;Set Function result on the stack
  729.         JMP        (A0)        ;Jump to the next jGNEFilter
  730.     }
  731.  
  732. }
  733.  
  734. Since a jGNEFilter has access to the actual event record that will be
  735. returned to the application, the filter can alter the event.  The most
  736. common thing to do is to 'cancel' an event by changing it to a null event
  737. (Ex. theEvent->what = nullEvent).  In this case it should also set the
  738. value in register D0 to False, or zero.
  739.  
  740. Here are some additional methods for an extension to get time periodically:
  741.  
  742. If your extension needs more frequent or regular time then can be provided
  743. by a jGNEFilter then you can install a VBL task or a time manager task. 
  744. Since these run at interrupt time they cannot do anything that could move
  745. or purge memory.  Other methods include patching a trap that is called
  746. frequently, like SetPort.
  747.  
  748. A faceless Notification Manager request is another method to get time. 
  749. This is a request that has all the fields in the notification record set to
  750. NULL except the nmResp field that holds the address of your routine to be
  751. executed.  Your notification response routine will be called soon and will
  752. be able to move memory.  If necessary the routine can reinstall itself. 
  753. This technique is useful for tasks that need to be executed once or
  754. intermittently.  For those tasks that need to be executed regularly use one
  755. of the other techniques.
  756.  
  757. You could of course have a faceless NM request that is installed by a VBL
  758. task or a time manager task.  
  759.  
  760. Use of a faceless background application in concert with an extension is
  761. yet another method to get time.  The fba would do its work on its null
  762. events.
  763.  
  764. --
  765. [11] How should an INIT manage memory?
  766.  
  767. In general, at INIT time extensions will want to allocate memory in the
  768. system heap.  If you are allocating pointers or handles, use the SYS
  769. variants, like NewHandleSys() and NewPtrSys().  It is a common error to
  770. forget this and then to wonder why the INIT crashes.  If you call NewHandle
  771. at INIT time, the handle will be allocated in the temporary heap allocated
  772. for your extension.  If your INIT attempts to use it after INIT time the
  773. temporary heap will be long gone, along with any handles or pointers that
  774. had been allocated in it.  Any attempt to use handles or pointers that no
  775. longer exist are predictably unpredictable :-) 
  776.  
  777. NewHandle and NewPtr will allocate their memory blocks in the system heap
  778. if the zone has been set to the system heap, as shown in the next
  779. paragraph.
  780.  
  781. If you need to read in resources you can ensure that they go into the
  782. system heap with something like the following code:
  783.  
  784. THz    saveZone = GetZone();
  785.     SetZone( SystemZone() );
  786.     //read in resources
  787.     SetZone( saveZone );
  788.  
  789. You will of course need to detach the resources if they need to remain in
  790. memory after your extension exits.  The resource file containing your
  791. extension will be closed when your extension exits.
  792.  
  793. If you need to read resources into memory after INIT time you need to
  794. decide which heap they should go into, either the application heap or the
  795. system heap.  It's a bit hard to make a specific recommendation on this but
  796. if the resource is something that the application is expecting to be read
  797. in then it should go into the application heap.  This would include things
  798. like WIND resources in a trap patch to GetNewWindow().  The problem of
  799. course is that the application heap may not have enough room.  However, if
  800. the resources are private to the extension then they should go into the
  801. system heap.
  802.  
  803. It should go without saying that you should always check the error codes on
  804. memory allocating calls and resource manager calls.  If your extension is
  805. doing something in response to a user action, say making a network
  806. connection, then it is appropriate to report such errors.  In many cases
  807. however, your extension will simply do nothing in the case of out of memory
  808. errors or missing resource errors.  It is best to attempt to allocate all
  809. of these things at INIT time and if unsuccessful to bail out then.
  810.  
  811. As mentioned above, MoveHHi doesn't work in the system heap.  If you intend
  812. to allocate a handle that will remain locked for extended periods, then
  813. call ResrvMem before allocating and locking the handle.  This will place
  814. the handle low in the system heap and help to prevent heap fragmentation. 
  815.  
  816. Many toolbox calls are documented as not moving or purging memory and as
  817. being safe to call at interrupt time.  If you are patching one of these
  818. traps then you must preserve this property.  You are guaranteed to cause
  819. other software to crash if you don't, and your users will hate you (once
  820. they figure out that it's you).  Be aware that the only memory manager
  821. routine safe to call under these circumstances is BlockMove. Also be aware
  822. that it is unsafe to access an unlocked handle at interrupt time.  It is
  823. possible that the memory manager is in the midst of moving it from one
  824. place to another in the heap, and the master pointer may not be updated
  825. yet.
  826.  
  827. --
  828. [12] How do I get my INIT to turn itself off?
  829.  
  830. An extension might want to turn itself off at INIT time based on its
  831. preferences setting, or based on a key being pressed or the mouse button
  832. being pressed, or due to an error during initialization.  Extensions
  833. usually show an icon with a red X through it in this case.  An extension
  834. might also want to turn itself off temporarily after INIT time in response
  835. to its Control Panel.  For instance GateKeeper has an on/off switch that
  836. turns off virus checking for a set time.
  837.  
  838. The strategy for temporarily turning off an extension is simply to set a
  839. global flag and to check it from within the trap patches or other parts of
  840. the extension.  If the flag is off then the trap patch simply executes the
  841. previous trap.  It is generally unsafe to unpatch or patch traps after INIT
  842. time from an extension.  The reason for this is that if another extension
  843. patches the same trap after you have, then it will be jumping to your patch
  844. when it has completed its work.  If you have removed your patch then this
  845. calling chain will be disrupted and bad things will happen.  Also, the
  846. Finder patches various traps when it loads, which is after INIT time. 
  847. Disrupting those patches would be a very bad thing.
  848.  
  849. If an extension determines at INIT time that it isn't going to stay around
  850. then it shouldn't call DetachResource on itself.  It's best that your
  851. extension determine that anything it's dependent on, such as resources,
  852. specific system Managers, and sufficient memory, are present *before* it
  853. starts to patch traps and install drivers, jGNEFilters and so on.  It would
  854. be a very bad idea for an extension to not detach itself after it had
  855. already patched a trap if the trap patch resided in the extension.
  856.  
  857. Many extensions use a particular key press as a signal to indicate that the
  858. user wants them not to run.  Of course the system uses the shift key as a
  859. signal not to turn on any extensions so you can't use that.  Some
  860. extensions use the option or command keys for this purpose.  The problem
  861. with using those keys is that every time I rebuild the desktop those
  862. extensions are needlessly inactivated.  I recommend that the space bar be
  863. used for this purpose.  Here's some sample code:
  864.  
  865.  Boolean
  866.  SpaceBarIsDown(void)
  867.  {
  868.      KeyMap        theKeys;
  869.  
  870.     GetKeys( theKeys );
  871.  
  872.     if ( theKeys[1] & 0x00000200 )//Check for spacebar
  873.         return TRUE;
  874.     else
  875.         return FALSE;
  876.  
  877.  }
  878.  
  879. --
  880. [13] How do I get my INIT to show it's icon like all the other cool inits
  881. do?
  882.  
  883. This is easy.  There is code available that does this for you.  Get a
  884. package written by Jim Walker called ShowIcon7 at:
  885. ftp://mac.archive.umich.edu/mac/development/source/showicon7.sit.hqx
  886.  
  887. You use it essentially as a plug-in.  Just pass in the icon's resource ID
  888. and it does everything for you.  It also shows how to set up an A5 world in
  889. an extension.  You might also take a look at Dair Grant's Extension shell
  890. package.  This one shows how to do animated icons.
  891.  
  892. If your extension decides that it can't install itself then it passes the
  893. resource ID of an icon that has a red X through it to the showicon7 code
  894. resource.
  895.  
  896. Some extensions include the ShowIcon code within their own code resources. 
  897. This code is only about 1K but it seems pointless to me for this code to
  898. sit in the system heap when it doesn't have to be.  Use it as a plug-in for
  899. your extensions.
  900.  
  901. --
  902. [14] How do I show a dialog from my INIT?
  903.  
  904. First of all, I hate windows of any kind during the startup process.  If
  905. all you want to do is show an alert then use the Notification Manager. 
  906. Your alert will show up when the Finder starts but the user will see it and
  907. will get whatever message you need to send.  
  908.  
  909. The problem I have with windows at INIT time is that they slow down this
  910. process and require user interactivity in a process that shouldn't do so. 
  911. Consider the computer that is on 24 hours a day doing something important
  912. unattended.  The power goes off and when it comes back on the machine
  913. reboots.  The user returns several hours later to find that the machine is
  914. still in the middle of its startup because your alert is waiting for a
  915. response.  Consider also the hapless INIT writer who has to reboot his
  916. machine 20 times a day.  Your INIT will not last long on my, um, his
  917. machine.
  918.  
  919. One additional annoyance is that you need to call InitWindows to show your
  920. window and this erases all the nice INIT icons on the screen.  I hate that.
  921.  
  922. Here are a few possible alternatives.  
  923.  
  924. * Play a sound or use the Speech Manager to communicate the information.  
  925.  
  926. * Show a different icon during startup to indicate an error.  An icon with
  927. a red X through it is one way to do this.  You could also use animated
  928. icons.
  929.  
  930. * If you must put up an alert then use a timer so that the alert goes away
  931. by itself, even if the OK button isn't clicked.
  932.  
  933. * If you need to interact with the user to get some information, say a
  934. password for a network connection, then do this once and save the results
  935. in a preferences file.  Provide a Control Panel to change the information. 
  936. Think of Control Panels as the interface for extensions.
  937.  
  938. * Store descriptions of any errors that occur in a preferences file.  Have
  939. the Control Panel display this information.  Remember to clear this
  940. information on each restart and to indicate to the user by sound or icon
  941. that an error has occurred.
  942.  
  943. * If you need to communicate error information to the user after INIT time
  944. then you should definitely use the Notification Manager.  For example,
  945. MacSLIP uses the NM to show an alert indicating that the carrier has been
  946. lost.
  947.  
  948. If you still want to show a dialog at INIT time then you need to set up an
  949. A5 world first.  The ShowIcon7 code mentioned above shows how to do this. 
  950. Also see the Tech Note 'Stand-Alone Code' (#256) for an explanation and
  951. sample code for this and 'Giving the (Desk)Hook to INITs' (247) discusses a
  952. bug that can appear when showing windows from extensions.
  953. ftp://ftp.apple.com/dts/mac/tn/operating.system.os/os-02-deskhook-and-init.hqx
  954.  
  955. If you do use the Notification Manager from an extension to indicate that
  956. the extension couldn't load you should use a self-disposing Notification
  957. request.  There are several strategies for doing this.  I have some code
  958. samples for this that will be (have been?) posted to alt.sources.mac soon.
  959.  
  960. --
  961. [15] How do I maintain compatibility with future systems?
  962.  
  963. This is tough, and the short answer is that you probably don't.  You'll
  964. notice that Apple comes out with new versions of its extensions with each
  965. revision of the system.  Apple's extensions also eventually disappear as
  966. their functionality is rolled into the system.  In all likelihood you'll
  967. have to come out with new versions of your extensions as new versions of
  968. the system come out as well.
  969.  
  970. Having said all that, there are things you can do to minimize this problem.
  971.  Here are a few suggestions:
  972.  
  973. * Minimize your reliance on undocumented features of the system.
  974.  
  975. * Minimize your reliance on low memory globals.  Use the Universal Header
  976. access 'functions' for accessing the low memory globals if necessary.
  977.  
  978. * Use system features like Gestalt, the Process Manager, and the
  979. Notification Manager to get information about the system, and to
  980. communicate with the user.
  981.  
  982. * Try to patch as few traps as possible and use non-patching methods
  983. whenever possible (e.g., use a jGNEFilter instead of patching
  984. WaitNextEvent; the filter is more likely to remain compatible than your
  985. patch).  
  986.  
  987. * Don't use self-modifying code.  Self-modifying code changes the
  988. instructions from what they were compiled as, to something else, at
  989. run-time.  The classic example is to change the address in a JMP
  990. instruction at run-time so that it jumps to the address of the previous
  991. trap.  This may seem faster than using a global variable but it is only
  992. slightly faster.  It is harder to write, debug, and maintain self-modifying
  993. code, and it is definitely more likely to break with new system releases. 
  994. If you do use self-modifying code, remember to flush the cache.  See the
  995. tech note 'Cache As Cache Can' (#261) for more information on that subject.
  996.  
  997. * Use the Universal Headers for writing your extensions.  This will help to
  998. ease the transition when it comes.
  999.  
  1000. --
  1001. [16] Any tips for INIT writing?
  1002.  
  1003. You may not want your INIT to actually do anything until after INIT time. 
  1004. You can find the end of INIT time if you have a jGNEFilter installed when
  1005. the first null event occurs.  The Notification Manager doesn't usually
  1006. start processing requests until INIT time is over so posting a faceless
  1007. notification request is another method to find the end of INIT time
  1008. (probably the best if you don't need a jGNEFilter for something else).  You
  1009. can also patch Launch to find the end of INIT time but this is trickier.
  1010.  
  1011. Unfortunately some extension writers do put up windows during INIT time. 
  1012. Because of this it's possible that events will occur before INIT time is
  1013. over.  To fail-safe the above approaches you also need to check for the
  1014. presence of the Process Manager with a Gestalt call.  The Process Manager
  1015. isn't available until after INIT time.  If Gestalt reports that the Process
  1016. Manager is not available then you need to reinstall your NM request, or
  1017. simply wait for another event to be reported to your jGNEFilter when the
  1018. Process Manager is available.
  1019.  
  1020. Here are some utility routines that can reduce the need for 68K assembler
  1021. in your extensions:
  1022.  
  1023. pascal void    SetA0( void* ) = { 0x205F };
  1024. pascal void    SetA1( void* ) = { 0x225F };
  1025. void *    GetA0( void ) = { 0x2008 };
  1026. void *    GetA7( void ) = { 0x200F };
  1027.  
  1028.  
  1029. -----------------------------------------------------------------------
  1030.  
  1031. Trap Patches:
  1032.  
  1033. --
  1034. [17] What exactly is a trap patch?
  1035.  
  1036. A trap patch is a method for changing the functionality of a trap.  The
  1037. addresses of all the traps are maintained in the two trap dispatch tables. 
  1038. By using the routine NSetTrapAddress and friends you can change the address
  1039. of a particular trap to code that you provide.  When this is done at INIT
  1040. time, all calls to the patched trap from all applications will go to your
  1041. code, which in most cases will do something and then call through the
  1042. existing trap in the ROMs.  Be warned that the Finder patches some traps
  1043. when it starts up in a way that prevents previously-installed patches from
  1044. executing.  
  1045.  
  1046. I recommend that you read the descriptions of the trap dispatch mechanism
  1047. in the 'Using Assembly Language' chapters in IM I and IV and also in the
  1048. 'Trap Manager' chapter in NIM Operating System Utilities.
  1049.  
  1050. Traps come in several types based on their parameter passing conventions. 
  1051. Most toolbox traps use pascal calling conventions, which means that all
  1052. parameters are passed on the stack and the return value, if any, is placed
  1053. on the stack.  
  1054.  
  1055. Some traps use register-based calling conventions.  In these traps the
  1056. parameters are passed in registers and the return value is returned in a
  1057. register, usually D0.  For example all the Memory Manager traps are
  1058. register-based and the File Manager traps are also register-based.  
  1059.  
  1060. Some traps are selector-based.  There are only so many spots in the
  1061. trap-dispatch tables.  In order to preserve space in these tables
  1062. selector-based traps have been developed.  In these traps a single trap
  1063. serves as the front end for a number of system routines.  The parameters of
  1064. these traps are passed in the usual manner, either on the stack or in
  1065. registers, and a selector is also passed, usually in a register.  When the
  1066. trap is called it checks the selector and then dispatches to the
  1067. appropriate routine.  Patching each of these types of traps involves
  1068. different mechanisms.  We'll look at samples of each one.
  1069.  
  1070. Patching traps on the PowerMac is a bit different than on the 68K Macs. 
  1071. Most of the discussion here is aimed at patching traps on the 68K Macs. 
  1072. Hopefully I'll learn some more about this subject soon and there will be
  1073. some better info here on patching traps on the PowerMac.
  1074.  
  1075. --
  1076. [18] What's the difference between a head patch and a tail patch?
  1077.  
  1078. It is most common to add some functionality to a trap when patching it
  1079. rather than just replacing the existing trap.  For instance I've written an
  1080. extension that speaks the text in alerts by using the Speech Manager.  This
  1081. works by patching Alert and friends.  When Alert is called the patch gets
  1082. the text that appears in the alert and passes it to the Speech Manager. 
  1083. The patch then calls the existing Alert trap that is in the ROMs.  A patch
  1084. that works in this way is called a head patch; it does its business and
  1085. then it calls the previous trap.
  1086.  
  1087. A tail patch is a bit different.  A virus-checking program might want to
  1088. patch GetResource and then examine the resource that was read in to see if
  1089. it contains a virus.  In order to do this the patch must first call the
  1090. existing trap and then do its processing, and finally return to the
  1091. application.  This is a tail patch because some processing occurs after the
  1092. existing trap is called.  In order for a patch to be a head patch you must
  1093. use a jmp instruction to jump to the previous trap.  If you use a jsr or a
  1094. C function pointer to jump to the previous trap you have a tail patch.
  1095.  
  1096. The reason that this head and tail patch business has been so important in
  1097. the past is because of an Apple invention called the come-from patch. 
  1098. Patches were invented, of course, so that Apple could fix bugs in the ROMs
  1099. and could update the routines in the ROMs with software.  This is why you
  1100. can run system 7 on a Mac Plus, whose ROMs are obviously missing most of
  1101. the additions made to the toolbox in recent years.  
  1102.  
  1103. Certain ROM routines are particularly large so it is inconvenient to patch
  1104. them if they have bugs in them.  To get around this problem the Apple
  1105. programmers searched for smaller routines that are called from the large
  1106. buggy routines and placed patches in the smaller routines.  These patches
  1107. check the return address on the stack.  If it is the address of the buggy
  1108. routine then a fix is applied.  If not then they just go on as usual.  The
  1109. patches to these smaller routines are known as come-from patches.  If you
  1110. tail patch one of these and then call the existing come-from patch, the
  1111. return address on the stack will be in your patch and not the buggy routine
  1112. that called you.  In this case the come-from patch will not apply its fix
  1113. and your system will crash.  
  1114.  
  1115. The good news is that as of system 7 it is safe to apply tail patches.  The
  1116. come-from patches still exist in system software, but NGetTrapAddress has
  1117. been modified to return an address that is safe to use when applying
  1118. tail-patches.  However, if you wish your extension to run in System 6 then
  1119. tail-patches are not allowed.  This is documented in the Trap Manager
  1120. chapter in NIM: OS Utilities.
  1121.  
  1122. If you absolutely positively need a tail patch in System 6 then the
  1123. following logic may apply: (Just don't tell anyone that I told you this :-)
  1124.  Apple is not releasing any new versions of System 6 so no new come-from
  1125. patches will be forthcoming for System 6.  If you do careful testing of the
  1126. traps you wish to tail-patch you will probably be OK.  In general traps not
  1127. called from the ROMs, like Alert and MenuKey, will not contain come-from
  1128. patches.
  1129.  
  1130. One final thing: In system 7 it is safe to apply tail-patches to all traps
  1131. except FrontWindow.
  1132.  
  1133. --
  1134. [19] How do I patch a trap?
  1135.  
  1136. Here is some sample code for a head patch of Alert, a stack-based trap:
  1137.  
  1138. TrapPtr    gOldAlertTrapAddress;
  1139.  
  1140. /****InstallPatch***************************************************/
  1141.  
  1142. void 
  1143. InstallPatch (void)
  1144. {
  1145.     gOldAlertTrapAddress = GetToolTrapAddress( _Alert );
  1146.     SetToolTrapAddress( (long) AlertPatch, _Alert );
  1147. }
  1148.  
  1149. /****AlertPatch***************************************************/
  1150.  
  1151. pascal void 
  1152. AlertPatch( short alertID, ProcPtr filterProcPtr )
  1153. {
  1154.     SetUpA4();
  1155.  
  1156.     MyAlert( alertID) ;    //Do our thing
  1157.  
  1158.     //store the correct alert addr
  1159.     //in A0 while we can still access globals via A4
  1160.  
  1161.     asm    { move.l    gOldAlertTrapAddress, A0 }
  1162.  
  1163.     RestoreA4();
  1164.     
  1165.     asm    {
  1166.             unlk        A6    //match the link generated by C
  1167.             jmp        (A0)    //jump to _Alert
  1168.         }
  1169. }
  1170.  
  1171. There are a number of details to note here.  This patch is of course part
  1172. of a code resource that is loaded at INIT time and detached as described in
  1173. an earlier section.  The routine InstallPatch must be called at INIT time. 
  1174.  
  1175.  
  1176. The routines GetToolTrapAddress and SetToolTrapAddress allow you to get and
  1177. set the addresses of Tool Traps.  The similar routines GetOSTrapAddress and
  1178. SetOSTrapAddress allow you to manipulate the addresses of OS traps.  The
  1179. routines NGetTrapAddress and NSetTrapAddress allow you to manipulate the
  1180. addresses of either, although they call glue code.  I recommend that you
  1181. use the GetXTrapAddress and SetXTrapAddress calls.  There are two obsolete
  1182. calls: GetTrapAddress and SetTrapAddress.  Don't use them.
  1183.  
  1184. The prototype for this patch is declared as 'pascal void' while the
  1185. prototype for Alert is 'pascal short'.  Because this is a head patch it
  1186. will not be returning a result; the result will be returned from the real
  1187. Alert trap.  The pascal keyword is used to indicate pascal calling
  1188. conventions.  It is not strictly required in all cases but does no harm.  
  1189.  
  1190. The Think C routines SetUpA4 and RestoreA4 are called to allow access to
  1191. global variables by A4 addressing.  In this case gOldAlertTrapAddress is
  1192. the only global variable we are addressing, unless any are used inside
  1193. MyAlert.  Note that this variable is moved to A0 while access to global
  1194. variables is still available.  In some cases one might move a global
  1195. variable to a local variable, which doesn't rely on A4 addressing.  A4
  1196. could then be restored and the old trap address could be loaded into A0
  1197. later in the code. 
  1198.  
  1199. Because there is a parameter list the compiler generates a Link A6
  1200. instruction at the start of this function.  In order to restore the stack a
  1201. matching Unlk A6 must be placed at the end of the function.  Since we are
  1202. exiting by the jmp (A0) we must insert the Unlk A6 ourselves.  The compiler
  1203. does generate an Unlk A6 and an RTS at the end of this function, but they
  1204. will never be executed.  The presence of the Link A6 instruction is
  1205. dependent on the particular compiler you use and on its rules for
  1206. generating a stack frame.  It is a good idea to disassemble the code for
  1207. your trap patches to see whether a stack frame has been generated in order
  1208. to determine if you need to insert the Unlk A6 instruction.  If you don't
  1209. match the Link A6 with an Unlk A6 the stack will be screwed up.
  1210.  
  1211. It is essential that the stack look exactly the same on exit from a head
  1212. patch as it does on entry.  If not you will surely crash.  (If you had good
  1213. reason you could modify the value of a parameter on the stack, but that's
  1214. another story.)  This patch saves and restores A4 but does modify A0 and A1
  1215. (SetUpA4 uses A1).  In general, with head patches of stack-based traps you
  1216. can modify A0, A1, D0, D1, and D2, but not any other registers.  You may
  1217. need to look at the disassembled code to be sure that all your registers
  1218. are properly saved and restored.  
  1219.  
  1220. The design of the patch as shown here, with the patch code calling a
  1221. separate function to perform the actual functionality of the patch is a
  1222. good design to follow with all but the simplest of patches.
  1223.  
  1224. You might think that you need to call StripAddress on the address of the
  1225. patch routine before passing this address to SetToolTrapAddress.  This is
  1226. not necessary unless the address is actually a handle.  If you were to load
  1227. a code resource and pass its entry point to SetToolTrapAddress then it
  1228. would need to be stripped.
  1229.  
  1230. --
  1231. [20] How do I patch a register-based trap?
  1232.  
  1233. Here is the code for a sample register-based trap patch:
  1234.  
  1235. TrapPtr        gMountVolAddress;
  1236.  
  1237. /****InstallPatch**************************************************/
  1238.  
  1239. void 
  1240. InstallPatch(void)
  1241. {
  1242.     gMountVolAddress =  GetOSTrapAddress( _MountVol );
  1243.     SetOSTrapAddress( (long) MountVolPatch, _MountVol );
  1244. }
  1245.  
  1246. /****MountVolPatch**************************************************
  1247.  
  1248. This is a register-based trap that has A0 set to point to its parameter
  1249. block on entry.  The  prototype for MountVol is:
  1250.  
  1251.     pascal OSErr PBMountVol( ParmBlkPtr paramBlock )
  1252.  
  1253. This patch beeps when a floppy or CD-ROM is inserted or when a 
  1254. harddrive is mounted by the Finder.
  1255.  
  1256. *******************************************************************/
  1257.  
  1258. pascal void 
  1259. MountVolPatch(void)
  1260. {
  1261. //Save some registers
  1262. //Save A0 since it's trashed by SetUpA4
  1263. //D1 contains the trap word
  1264.  
  1265.     asm    {    movem.l    a0/d0-d1, -(sp)        }
  1266.  
  1267.     SetUpA4();        //Allow access to global variables
  1268.  
  1269.     SysBeep( 5 );    //The guts of our head patch
  1270.     
  1271.     //store the correct MountVol addr
  1272.     //in A1 while we can still access globals via A4
  1273.     asm    {    move.l    gMountVolAddress, A1    }
  1274.  
  1275.     RestoreA4();    //Restore previous value in A4
  1276.     
  1277.     asm    {                    //Restore the registers
  1278.             movem.l    (sp)+, a0/d0-d1
  1279.             jmp        (A1)        //jump to _MountVol
  1280.         }
  1281.  
  1282. }
  1283.  
  1284. This head patch is similar in structure to the patch to Alert with a few
  1285. differences.  The prototype uses no parameters and has no return value. The
  1286. single parameter is passed through A0.  This patch doesn't do anything with
  1287. this value but it could be moved to a local variable and then used to
  1288. reference the fields in the parameter block if desired.  Access to global
  1289. variables is by the same A4 mechanism as in the Alert patch.  Note that
  1290. _MountVol is an OS trap so GetOSTrapAddress and SetOSTrapAddress are used
  1291. to set up the patch.  
  1292.  
  1293. Since A0 is used to pass the parameter to this trap we jump to the real
  1294. _MountVol trap through A1.  
  1295.  
  1296. Obviously A0 must be saved and restored in this patch. OS traps expect to
  1297. find the trap word in D1 so it must be saved and restored as well.  Three
  1298. registers, A0, D0, and D1, are saved onto the stack with the movem
  1299. instruction, and restored at the end of the patch.  
  1300.  
  1301. Think C doesn't generate a 'Link A6' at the start of this function because
  1302. there are no parameters and no local variables.  Because of this no 'Unlk
  1303. A6' is needed at the end of the function.
  1304.  
  1305. --
  1306. [21] Can you show me a tail patch?
  1307. Here is another example of a patch to a register-based trap.  This sample
  1308. is a tail patch and is dependent on the CodeWarrior environment.  Because
  1309. CW allows you to specify that parameters are passed in registers this trap
  1310. patch requires no assembly.
  1311.  
  1312. extern pascal OSErr (*Old_MountVol)( ParmBlkPtr pb : __A0 ) : __D0;
  1313.  
  1314. pascal OSErr My_MountVol( ParmBlkPtr pb : __A0 ) : __D0
  1315. {
  1316.     OSErr        err;
  1317.     long        saveA4 = SetCurrentA4();
  1318.  
  1319.     err = Old_MountVol( pb );
  1320.     DoSomthingFunc();
  1321.  
  1322.     SetA4( saveA4 );
  1323.     return err;
  1324. }
  1325.  
  1326. --
  1327. [22] How do I patch a selector-based trap?
  1328.  
  1329. Here is a sample patch to PrGlue.  This trap is the front end for all the
  1330. Printing Manager routines.  Its selector is pushed on the stack
  1331.  
  1332. typedef struct 
  1333. {
  1334.     long        Selector;
  1335.     THPrint    hPrint;
  1336. } PrJobDialogStack;
  1337.  
  1338. TrapPtr        PrGlueAddress;
  1339.  
  1340. /****InstallPatch****************************************************/
  1341.  
  1342. void 
  1343. InstallPatch(void)
  1344. {
  1345.     PrGlueAddress = GetToolTrapAddress( _PrGlue );
  1346.     SetToolTrapAddress( (long) PrGluePatch, _PrGlue );
  1347. }
  1348.  
  1349. /****PrGluePatch****************************************************
  1350.  
  1351. This is a stack-based trap with a long word selector also pushed 
  1352. onto the stack.  On entry the selector is at 4(A7).  The return address is
  1353. at 0(A7).  After the 'Link A6' the selector is at 12(A7).
  1354.  
  1355. *******************************************************************/
  1356.  
  1357. #define kSelectorOffset 12
  1358. #define kPrJobDialogSelector 0x32040488
  1359.  
  1360. pascal void 
  1361. PrGluePatch(void)
  1362. {
  1363.     PrJobDialogStack    *StackPtr;
  1364.  
  1365.     //Get address of the stack frame
  1366.     //and save it in a local variable
  1367.  
  1368.     asm    { 
  1369.             lea     kSelectorOffset(A7), A0
  1370.             move.l A0, StackPtr
  1371.         }
  1372.  
  1373.     SetUpA4();        //Allow access to global variables
  1374.  
  1375.     //Check the selector
  1376.     if ( StackPtr->Selector == kPrJobDialogSelector )
  1377.     {
  1378.         SysBeep( 5 );
  1379.  
  1380.         //Pass hPrint to our function to do something
  1381.         DoSomethingFunc( StackPtr->hPrint );
  1382.     }
  1383.  
  1384.     //Store the correct PrGlue addr
  1385.     //in A0 while can still access globals via A4
  1386.     asm    { move.l    PrGlueAddress, A0 }                                
  1387.     RestoreA4();    //Restore previous value in A4
  1388.     
  1389.     asm    {
  1390.             unlk        A6    //match C's Link A6
  1391.             jmp        (A0)    //jump to _PrGlue
  1392.         }
  1393.  
  1394. }
  1395.  
  1396. In order to access the selector and the parameters for PrGlue we use a
  1397. pointer to a struct.  Once the pointer is initialized correctly we can
  1398. access the selector and any parameters from C easily.
  1399.  
  1400. According to NIM: PPC System Software it is not safe to patch
  1401. selector-based traps with PPC native code.  All patches of selector-based
  1402. traps on the PowerMac should be written in 68K code.
  1403.  
  1404. --
  1405. [23] How do I patch a trap on the PPC?
  1406.  
  1407. See NIM 'PowerPC System Software' for a more complete discussion.  There is
  1408. also a new book by Tom Thomson called 'Power Macintosh Programming Starter
  1409. Kit' that has examples of how to patch traps on the PowerMac.  
  1410.  
  1411. Patching traps on the PowerMac is similar to patching on the 68K
  1412. architecture.  Of course you must generate a UniversalProcPtr for each of
  1413. your patches in the system heap, and these are then passed to the
  1414. SetXTrapAddress routines.  Since code fragments have their own globals the
  1415. use of A4 or A5-based mechanisms for accessing global variables isn't
  1416. needed.  In order to call the previous trap you need to call
  1417. CallUniversalProc or CallOSTrapUniversalProc and return its result from
  1418. your patch.  As a result all patches on the PowerMac are tail patches.
  1419.  
  1420. You cannot safely patch a selector-based trap in native code.  See NIM PPC
  1421. for an explanation of this.  Unless or until this changes you shoud do all
  1422. patching of selector-based traps in 68K code.
  1423.  
  1424. You may find an application called 'Traps Check' useful.  This app supplies
  1425. a report about all the traps on a Powermac, indicating whether each trap is
  1426. emulated or native.  Another way to do this is to drop into MacsBug and
  1427. disassemble from the address of the trap you're interested in (e.g., 'il
  1428. CopyBits' ).  For traps that are native you'll see a routine descriptor
  1429. that begins with the MixedModeMagic trap (AAFE). This of course won't tell
  1430. you if the trap has been patched.  You can identify a patch by whether it's
  1431. in RAM or ROM, from its address.  Determining whether a patched trap is PPC
  1432. native or not may take some additioinal sleuthing. You can find Traps Check
  1433. at:  ftp://sumex-aim.stanford.edu/info-mac/dev/traps-check-10.hqx
  1434.  
  1435. Here is a sample PowerMac trap patch for GetResource:
  1436.  
  1437. enum {
  1438.     uppGetResourceProcInfo= kPascalStackBased
  1439.          | RESULT_SIZE(SIZE_CODE(sizeof(Handle)))
  1440.          | STACK_ROUTINE_PARAMETER(1, SIZE_CODE(sizeof(ResType)))
  1441.          | STACK_ROUTINE_PARAMETER(2, SIZE_CODE(sizeof(short)))
  1442. };
  1443.  
  1444. UniversalProcPtr gGetResourceUPP;
  1445. UniversalProcPtr gGetResourcePatchUPP;
  1446.  
  1447. /****InstallPatch**************************************************/
  1448.  
  1449. void
  1450. InstallPatch(void)
  1451. {
  1452.     THz    saveZone = GetZone();
  1453.     SetZone( SystemZone() );    //put UPP in syszone
  1454.  
  1455.     gGetResourceUPP = GetToolTrapAddress( _GetResource );
  1456.  
  1457.     gGetResourcePatchUPP = 
  1458.         NewRoutineDescriptor( (ProcPtr) GetResourcePatch,
  1459.             uppGetResourceProcInfo, GetCurrentISA() );
  1460.  
  1461.     SetToolTrapAddress( gGetResourcePatchUPP, _GetResource );
  1462.  
  1463.     SetZone( saveZone );
  1464.  
  1465. }
  1466.  
  1467.  
  1468. /****GetResourcePatch*********************************************/
  1469.  
  1470. Handle 
  1471. GetResourcePatch( ResType theType, short theID )
  1472. {
  1473.     Handle    result;
  1474.  
  1475. //We don't need no 'DUMB' resources
  1476.     if ( theType == 'DUMB' )
  1477.         result = NULL;
  1478.     else
  1479.         result = (Handle) CallUniversalProc( gGetResourceUPP,
  1480.                 uppGetResourceProcInfo, theType, theID );
  1481.     
  1482.     return result;
  1483.  
  1484. }
  1485.  
  1486. This patch as written is PPC only for illustrative purposes.  In real life
  1487. the NewRoutineDescriptor and CallUniversalProc would be #defined in a
  1488. header file and would compile correctly for both 68K and PPC code.  You can
  1489. find examples of how to do this in the Universal header files.
  1490.  
  1491. --
  1492. [24] Can I write a fat trap?
  1493.  
  1494. //Under construction
  1495.  
  1496. --
  1497. [25] Tips?
  1498.  
  1499. If your patch isn't called you may have guessed wrong on whether it's a
  1500. ToolTrap or an OSTrap.  The high bit of the second byte of the trap word is
  1501. set for ToolTraps.  The following function can be used to get the correct
  1502. trap address for both ToolTraps and OSTraps.
  1503.  
  1504. pascal void * GetCurrentTrapAddress( unsigned short trapWord )
  1505. {
  1506.     if ( trapWord & 0x0800 )
  1507.         return GetToolTrapAddress ( trapWord & 0x07FF );
  1508.     else
  1509.         return GetOSTrapAddress ( trapWord & 0x07FF );
  1510. }
  1511.  
  1512.  
  1513. If the machine crashes after leaving your patch you have probably munged
  1514. the stack or not saved and restored all the registers that you must.
  1515.  
  1516. The Finder patches a number of traps when it loads in a way that prevents
  1517. earlier trap patches from functioning.  If your patch doesn't appear to be
  1518. called it may be one of these patches.
  1519.  
  1520. --
  1521. [26] What other sources of information are available?
  1522.  
  1523. Knaster 'How to Write Macintosh Software'
  1524. Knaster and Rollin, 'Macintosh Programming Secrets'.  These books on Mac
  1525. programming has some excellent info on trap patching.
  1526.  
  1527. Tom Thomson 'Power Macintosh Programming Starter Kit' This book has some
  1528. example code for writing trap patches and extensions on the PowerMac.
  1529.  
  1530. The Extension Shell package (by Dair Grant, dair@kagi.com) at:
  1531.   ftp://sumex-aim.stanford.edu/info-mac/dev/src/extension-shell-15.hqx
  1532.  
  1533. Usenet Macintosh Programmers Guide
  1534.   ftp://sumex-aim.stanford.edu/info-mac/dev/info/usenet-mac-prog-guide-msw.hqx
  1535.  
  1536. All of the Apple Tech Notes have been made available on Apple's web server:
  1537.   http://www.info.apple.com/dev/technotes/Main.html
  1538.